package main

import (
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/natefinch/lumberjack"
	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"net"
	"net/http"
	"net/http/httputil"
	"os"
	"runtime/debug"
	"strings"
	"time"
)

var Logger *zap.Logger
var SugarLogger *zap.SugaredLogger
var MyLogger *zap.Logger

func init() {
	var err error
	Logger, err = zap.NewProduction()
	if err != nil {
		fmt.Printf("zap init err:%v\n", err)
		return
	}

	Logger.Info("zap Logger init ok")

	SugarLogger = Logger.Sugar()

	SugarLogger.Info("zap SugaredLogger init ok")

	//encoder := zapcore.NewJSONEncoder(zap.NewProductionEncoderConfig())

	conf := zap.NewProductionEncoderConfig()

	conf.EncodeTime = zapcore.ISO8601TimeEncoder
	conf.EncodeLevel = zapcore.CapitalLevelEncoder

	encoder := zapcore.NewConsoleEncoder(conf)

	//file, _ := os.OpenFile("./test.log", os.O_APPEND, os.ModeAppend)

	// 切割日志
	lumberJackLogger := &lumberjack.Logger{
		Filename:   "./test.log",
		MaxSize:    1,  // M
		MaxBackups: 5,  // 备份数量
		MaxAge:     30, // 备份天数
		Compress:   false,
	}

	ws := zapcore.AddSync(lumberJackLogger)

	core := zapcore.NewCore(encoder, ws, zapcore.DebugLevel)
	MyLogger = zap.New(core, zap.AddCaller())

	MyLogger.Info("zap MyLogger init ok")

}

func simpleHttpGet(url string) {
	resp, err := http.Get(url)
	if err != nil {
		Logger.Error(
			"Error fetching url..",
			zap.String("url", url),
			zap.Error(err))

		MyLogger.Error(
			"Error fetching url..",
			zap.String("url", url),
			zap.Error(err))
	} else {
		Logger.Info("Success..",
			zap.String("statusCode", resp.Status),
			zap.String("url", url))

		MyLogger.Info("Success..",
			zap.String("statusCode", resp.Status),
			zap.String("url", url))
		resp.Body.Close()
	}
}

// gin 配置日志
func Gin() {

	//e := gin.Default()

	e := gin.New()
	e.Use(GinLogger(MyLogger), GinRecovery(MyLogger, true))
	e.GET("/hello", func(c *gin.Context) {

		c.JSON(http.StatusOK, "hello")

	})
	e.Run()

}

// GinLogger 接收gin框架默认的日志
func GinLogger(logger *zap.Logger) gin.HandlerFunc {
	return func(c *gin.Context) {
		start := time.Now()
		path := c.Request.URL.Path
		query := c.Request.URL.RawQuery
		c.Next()

		cost := time.Since(start)
		logger.Info(path,
			zap.Int("status", c.Writer.Status()),
			zap.String("method", c.Request.Method),
			zap.String("path", path),
			zap.String("query", query),
			zap.String("ip", c.ClientIP()),
			zap.String("user-agent", c.Request.UserAgent()),
			zap.String("errors", c.Errors.ByType(gin.ErrorTypePrivate).String()),
			zap.Duration("cost", cost),
		)
	}
}

// GinRecovery recover掉项目可能出现的panic
func GinRecovery(logger *zap.Logger, stack bool) gin.HandlerFunc {
	return func(c *gin.Context) {
		defer func() {
			if err := recover(); err != nil {
				// Check for a broken connection, as it is not really a
				// condition that warrants a panic stack trace.
				var brokenPipe bool
				if ne, ok := err.(*net.OpError); ok {
					if se, ok := ne.Err.(*os.SyscallError); ok {
						if strings.Contains(strings.ToLower(se.Error()), "broken pipe") || strings.Contains(strings.ToLower(se.Error()), "connection reset by peer") {
							brokenPipe = true
						}
					}
				}

				httpRequest, _ := httputil.DumpRequest(c.Request, false)
				if brokenPipe {
					logger.Error(c.Request.URL.Path,
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)
					// If the connection is dead, we can't write a status to it.
					c.Error(err.(error)) // nolint: errcheck
					c.Abort()
					return
				}

				if stack {
					logger.Error("[Recovery from panic]",
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
						zap.String("stack", string(debug.Stack())),
					)
				} else {
					logger.Error("[Recovery from panic]",
						zap.Any("error", err),
						zap.String("request", string(httpRequest)),
					)
				}
				c.AbortWithStatus(http.StatusInternalServerError)
			}
		}()
		c.Next()
	}
}

func main() {

	defer Logger.Sync()
	fmt.Printf("init Logger = %v\n", Logger)

	simpleHttpGet("https://www.baidu.com")
	simpleHttpGet("https://www.google.com")

	// 测试日志切割
	//for i := 0; i < 40000; i++ {
	//	MyLogger.Info("test for lumberjack ")
	//}

	Gin()

}
